package com.sparkseries.service.impl;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.sparkseries.common.util.exception.BusinessException;
import com.sparkseries.dao.SysMenuMapper;
import com.sparkseries.dao.SysRoleMapper;
import com.sparkseries.dao.SysUserMapper;
import com.sparkseries.domain.SysRole;
import com.sparkseries.domain.SysUser;
import com.sparkseries.domain.vo.SysRoleVO;
import com.sparkseries.domain.vo.SysUserVO;
import com.sparkseries.dto.SysRoleDTO;
import com.sparkseries.enums.DefaultStatusEnum;
import com.sparkseries.service.SysRoleService;
import com.sparkseries.common.util.domain.Result;
import com.sparkseries.common.util.tool.ObjectConvertTool;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 角色管理
 * TODO 优化参数校验
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysRoleServiceImpl implements SysRoleService {
    private final SysRoleMapper sysRoleMapper;
    private final SysUserMapper sysUserMapper;
    private final SysMenuMapper sysMenuMapper;

    /**
     * 创建角色
     * @param roleDTO 角色传输类
     * @return 默认响应类
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<?> createRole(SysRoleDTO roleDTO) {
        validateRoleDTO(roleDTO);

        SysRole sysRole = convertToSysRole(roleDTO);
        sysRole.setId(IdWorker.getId());

        if (sysRoleMapper.createRole(sysRole) <= 0) {
            log.error("角色创建失败，角色信息: {}", roleDTO);
            throw new BusinessException("角色创建失败");
        }

        log.info("角色创建成功，角色ID: {}", sysRole.getId());
        return Result.ok("角色创建成功");
    }

    /**
     * 绑定用户角色
     * @param userIds 用户ID集合
     * @param roleId 角色ID
     * @return 默认响应类
     */
    @Override
    public Result<?> bindUserWithRole(List<Long> userIds, Long roleId) {
        validateUserIds(userIds);
        validateRoleId(roleId);

        if (sysRoleMapper.bindUserWithRole(userIds, roleId) <= 0) {
            log.error("用户角色绑定失败，角色ID: {}，用户ID: {}", roleId, userIds);
            throw new BusinessException("绑定失败");
        }

        log.info("用户角色绑定成功，角色ID: {}，用户ID: {}", roleId, userIds);
        return Result.ok("绑定成功");
    }

    /**
     * 删除角色
     * @param roleId 角色ID
     * @return 默认响应类
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<?> deleteRole(Long roleId) {
        validateRoleId(roleId);

        // 检测该角色是否分配给了用户
        List<SysUser> sysUsers = sysRoleMapper.listUsersWithRole(roleId);
        if (!sysUsers.isEmpty()) {
            log.error("该角色已经被分配，角色ID: {}", roleId);
            throw new BusinessException("该角色已经被分配");
        }

        // 删除角色
        if (sysRoleMapper.deleteRole(roleId) <= 0) {
            log.error("角色删除失败，角色ID: {}", roleId);
            throw new BusinessException("角色删除失败");
        }

        // 清理关联数据
        cleanupUserAssociations(roleId);

        log.info("角色删除成功，角色ID: {}", roleId);
        return Result.ok("角色删除成功");
    }

    /**
     * 清理角色关联数据（菜单绑定）
     */
    private void cleanupUserAssociations(Long roleId) {
        Integer unbindCount = sysMenuMapper.deleteRoleBind(roleId);
        log.debug("已移除了角色: {}的{}条菜单绑定", roleId, unbindCount);
    }

    /**
     * 删除用户角色绑定
     * @param userIds 用户ID集合
     * @param roleId 角色ID
     * @return 默认响应类
     */
    @Override
    public Result<?> deleteUserWithRole(List<Long> userIds, Long roleId) {
        validateUserIds(userIds);
        validateRoleId(roleId);

        if (sysRoleMapper.deleteRoleBindR(userIds, roleId) <= 0) {
            log.error("用户角色绑定删除失败，角色ID: {}，用户ID: {}", roleId, userIds);
            throw new BusinessException("删除失败");
        }

        log.info("用户角色绑定删除成功，角色ID: {}，用户ID: {}", roleId, userIds);
        return Result.ok("删除成功");
    }

    /**
     * 修改角色信息
     * @param roleDTO 角色传输类
     * @return 默认响应类
     */
    @Override
    public Result<?> updateRole(SysRoleDTO roleDTO) {
        validateRoleDTO(roleDTO);
        validateRoleId(roleDTO.getId());

        SysRole role = convertToSysRole(roleDTO);
        if (sysRoleMapper.updateRole(role) <= 0) {
            log.error("角色修改失败，角色信息: {}", role);
            throw new BusinessException("角色信息修改失败");
        }

        log.info("角色修改成功，角色ID: {}", role.getId());
        return Result.ok("角色信息修改成功");
    }

    /**
     * 修改角色状态
     * @param roleId 角色ID
     * @param status 是否启用（0-禁用，1-启用）
     * @return 默认响应类
     */
    @Override
    public Result<?> updateRoleStatus(Long roleId, Integer status) {
        validateRoleId(roleId);
        validateStatus(status);

        if (sysRoleMapper.updateRoleStatus(roleId, status) <= 0) {
            log.error("角色状态修改失败，角色ID: {}，状态: {}", roleId, status);
            throw new BusinessException("角色状态修改失败");
        }

        log.info("角色状态修改成功，角色ID: {}，状态: {}", roleId, DefaultStatusEnum.getDesc(status));
        return Result.ok("角色状态修改成功");
    }

    /**
     * 获取角色列表
     * @param roleDTO 角色传输类
     * @return 角色响应类集合
     */
    @Override
    public Result<List<SysRoleVO>> listRole(SysRoleDTO roleDTO) {
        validateRoleDTO(roleDTO);

        SysRole role = convertToSysRole(roleDTO);
        List<SysRole> roles = sysRoleMapper.listRole(role);
        List<SysRoleVO> roleVOs = ObjectConvertTool.originsConvert(roles, SysRoleVO.class);

        return Result.ok("查询成功", roleVOs);
    }

    /**
     * 获取角色信息
     * @param roleId 角色ID
     * @return 角色响应类
     */
    @Override
    public Result<SysRoleVO> getRoleInfo(Long roleId) {
        return Optional.ofNullable(sysRoleMapper.getRoleById(roleId))
                .map(role -> {
                    log.debug("成功获取角色信息，角色ID: {}", roleId);
                    return Result.ok("查询成功", convertToSysRoleVO(role));
                })
                .orElseGet(() -> {
                    log.warn("未查询到该角色，角色ID: {}", roleId);
                    return Result.error("角色信息未找到");
                });
    }

    /**
     * 获取该角色下的用户
     * @param roleId 角色ID
     * @return 用户响应类
     */
    @Override
    public Result<List<SysUserVO>> listUserWithRole(Long roleId) {
        validateRoleId(roleId);

        List<SysUser> users = sysRoleMapper.listUsersWithRole(roleId);
        if (users.isEmpty()) {
            log.info("角色下无用户，角色ID: {}", roleId);
            return Result.ok("角色下无用户");
        }

        List<SysUserVO> userVOs = ObjectConvertTool.originsConvert(users, SysUserVO.class);
        return Result.ok("查询成功", userVOs);
    }

    //------------------------ 参数校验 ------------------------
    private void validateRoleDTO(SysRoleDTO roleDTO) {
        if (Objects.isNull(roleDTO)) {
            throw new BusinessException("角色信息不能为空");
        }
    }

    private void validateRoleId(Long roleId) {
        if (Objects.isNull(roleId) || roleId <= 0) {
            throw new BusinessException("角色ID无效");
        }
        if (Objects.isNull(sysRoleMapper.getRoleById(roleId))) {
            throw new BusinessException("角色不存在");
        }
    }

    private void validateUserIds(List<Long> userIds) {
        if (Objects.isNull(userIds) || userIds.isEmpty()) {
            throw new BusinessException("用户ID为空");
        }
        for (Long userId : userIds) {
            if (Objects.isNull(sysUserMapper.getUserById(userId))) {
                throw new BusinessException("用户不存在，用户ID: " + userId);
            }
        }
    }

    private void validateStatus(Integer status) {
        if (Objects.isNull(status)) {
            throw new BusinessException("状态值为空");
        }
        if (!DefaultStatusEnum.isValid(status)) {
            throw new BusinessException("无效的角色状态");
        }
    }

    //------------------------ 工具方法  ------------------------
    // 将 DTO 转换为实体
    private SysRole convertToSysRole(SysRoleDTO roleDTO) {
        return ObjectConvertTool.originConvert(roleDTO, SysRole.class);
    }

    // 将实体转换为 VO
    private SysRoleVO convertToSysRoleVO(SysRole sysRole) {
        return ObjectConvertTool.originConvert(sysRole, SysRoleVO.class);
    }
}